package com.ls.jpushMessageClient;

/**
 * 极光推送工具类
 * 利用别名alias 推送默认一次最多可以推送1000个 可以找激光商务调整
 * 当然也可以利用别的推送，暂时只是用别名推送就足够啦.
 */

import cn.jiguang.common.ClientConfig;
import cn.jiguang.common.ServiceHelper;
import cn.jiguang.common.connection.ApacheHttpClient;
import cn.jiguang.common.connection.HttpProxy;
import cn.jiguang.common.resp.APIConnectionException;
import cn.jiguang.common.resp.APIRequestException;
import cn.jpush.api.push.PushResult;
import cn.jpush.api.push.model.Message;
import cn.jpush.api.push.model.Options;
import cn.jpush.api.push.model.Platform;
import cn.jpush.api.push.model.PushPayload;
import cn.jpush.api.push.model.audience.Audience;
import cn.jpush.api.push.model.notification.AndroidNotification;
import cn.jpush.api.push.model.notification.IosNotification;
import cn.jpush.api.push.model.notification.Notification;
import cn.jpush.api.push.model.notification.WinphoneNotification;
import com.alibaba.fastjson.JSONObject;
import com.ls.baseService.utils.Constants;
import org.apache.log4j.Logger;

import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

/**
 * jpush推送，以前推送默认采用http的方式
 * 现在增加了一些方式，本代码中采用netty的方式处理
 */
public class JPushUtils {
    protected static final Logger log = Logger.getLogger(JPushUtils.class);

    static final String PLATFORM_ANDROID="android";
    static final String PLATFORM_IOS="ios";
    static final String PLATFORM_WINPHONE="winphone";
    static final String PLATFORM_ALL="all";

    /**
     * 这块做一下说明，因为正常一个地区的app新包，只对应一个appKey，
     * 后来发现有一个地区对应对个不同的appKey，为了保持之前的逻辑，
     * 下面的map的key改为area+appKey，确保唯一性
     */
    public static Map<String,List<JPushClientCustom>> areaAppKeyPushClientMap=new ConcurrentHashMap<String,List<JPushClientCustom>>();
    public static Map<String,AreaConfig> areaAppKeyConfigMap=new ConcurrentHashMap<String,AreaConfig>();

    public static ScheduledExecutorService fixedTimeService = Executors.newSingleThreadScheduledExecutor();

    public static final int defaultJpushClientHandlerNum=200;

    public static final String AREAAPPKEY_INTERVAL="_";

    /**
     * 每个地区对应的推送消息
     * 包含每个地区的appKey，masterSecret
     * 还有需要监控的队列，当然多个地区也可以使用一个队列的
     */
    public static class AreaConfig{
        private String area;
        private String appKey;
        private String masterSecret;
        private boolean open;

        public String getArea() {
            return area;
        }

        public void setArea(String area) {
            this.area = area;
        }

        public String getAppKey() {
            return appKey;
        }

        public void setAppKey(String appKey) {
            this.appKey = appKey;
        }

        public String getMasterSecret() {
            return masterSecret;
        }

        public void setMasterSecret(String masterSecret) {
            this.masterSecret = masterSecret;
        }

        public boolean isOpen() {
            return open;
        }

        public void setOpen(boolean open) {
            this.open = open;
        }
    }

    //消费者启动之后加载多个地区的配置信息，建立多个地区的jpushclient实例，中途也可以加入进来
    public static void loadAreaConfig(JSONObject jsonObject){
        try {
            List<AreaConfig> areaConfigs = JSONObject.parseArray(jsonObject.getString("areaConfigs"), AreaConfig.class);
            if(areaConfigs!=null && areaConfigs.size()>0){
                for(AreaConfig areaConfig:areaConfigs){
                    if(!areaConfig.isOpen())
                        continue;
                    //因为一个地区有多个appKey
                    String areaAppKey=AREAAPPKEY_INTERVAL+areaConfig.getArea()+AREAAPPKEY_INTERVAL+areaConfig.getAppKey();
                    AreaConfig temp = areaAppKeyConfigMap.get(areaAppKey);
                    if(temp==null){
                        areaAppKeyConfigMap.put(areaAppKey,areaConfig);
                    }else{
                        temp=areaConfig;
                    }
                    if(areaAppKeyPushClientMap.get(areaAppKey)==null){
                        List<JPushClientCustom> list=new Vector<JPushClientCustom>();
                        for(int i=0;i<1;i++){
                            JPushClientCustom jPushClientCustom = new JPushClientCustom(areaConfig.getAppKey(), areaConfig.getMasterSecret());
                            String authCode = ServiceHelper.getBasicAuthorization(areaConfig.getAppKey(), areaConfig.getMasterSecret());
                            //NettyHttpClient nettyHttpClient = new NettyHttpClient(authCode,(HttpProxy) null,ClientConfig.getInstance());
                            ApacheHttpClient apacheHttpClient = new ApacheHttpClient(authCode,(HttpProxy) null,ClientConfig.getInstance());
                            jPushClientCustom.getPushClient().setHttpClient(apacheHttpClient);
                            list.add(jPushClientCustom);
                        }
                        areaAppKeyPushClientMap.put(areaAppKey,list);
                    }
                }
            }

            fixedTimeService.scheduleWithFixedDelay(new Runnable() {
                @Override
                public void run() {
                    try {
                        if(areaAppKeyPushClientMap!=null && areaAppKeyPushClientMap.size()>0){
                            List<JPushClientCustom> value=null;
                            long currentTime = System.currentTimeMillis();
                            long difference=60*60*1000; //60分钟这个jpushclient 实例没用活动过啦
                            for (Map.Entry<String, List<JPushClientCustom>> entry : areaAppKeyPushClientMap.entrySet()) {
                                value = entry.getValue();
                                if(value!=null && value.size()>0){
                                    String format = String.format("{'key':'%s','size':%d}", entry.getKey(), value.size());
                                    if(log.getLevel()==null){
                                        System.out.println(format);
                                    }else{
                                        log.info(format);
                                    }
                                    if(value.size()<=1)
                                        continue;
                                    Iterator<JPushClientCustom> iterator = value.iterator();
                                    int index=0; //第一个元素不用管
                                    while (iterator.hasNext()){
                                        JPushClientCustom next = iterator.next();
                                        if(++index==1){
                                            continue;
                                        }
                                        if(next!=null && currentTime-next.getLast_handler_time()>=difference){
                                            next.close();
                                            //可能这个集合操作会发生并发错误,发生概率比较小，暂时不处理,集合使用verctor
                                            iterator.remove();
                                        }
                                    }
                                }
                            }

                        }
                    }catch(Exception e){
                        e.printStackTrace();
                    }
                }
            },1,60,TimeUnit.MINUTES);

        }catch (Exception e){
            e.printStackTrace();
        }
    }

    public static void relepaseJpushClient(){
        if(areaAppKeyPushClientMap!=null && areaAppKeyPushClientMap.size()>0) {
            Set<Map.Entry<String, List<JPushClientCustom>>> entries = areaAppKeyPushClientMap.entrySet();
            if(!entries.isEmpty()){
                Iterator<Map.Entry<String, List<JPushClientCustom>>> iterator = entries.iterator();
                if(iterator.hasNext()){
                    Map.Entry<String, List<JPushClientCustom>> next = iterator.next();
                    List<JPushClientCustom> value = next.getValue();
                    if(value!=null && value.size()>0){
                        for(JPushClientCustom j:value){
                            j.close();
                        }
                    }
                    iterator.remove();
                }
            }
        }
    }
    public static void closeJpushClient(JPushClientCustom jPushClientCustom){
       if(jPushClientCustom!=null)
           jPushClientCustom.close();
    }


    public static JPushClientCustom getJpushClient(String areaAppKey){
        JPushClientCustom jPushClientCustom=null;
        if(areaAppKeyPushClientMap.get(areaAppKey)==null){ //前面已经加载了不会走下面的
            AreaConfig areaConfig = areaAppKeyConfigMap.get(areaAppKey);
            if(areaConfig==null){
                log.info(areaAppKey+" config is null");
            }else if(areaConfig.getAppKey()!=null && areaConfig.getMasterSecret()!=null){
                //经测试高并发需求，可以使用ApacheHttpClient   NettyHttpClient会卡住，目前没有查找原因
                jPushClientCustom = new JPushClientCustom(areaConfig.getAppKey(), areaConfig.getMasterSecret());
                String authCode = ServiceHelper.getBasicAuthorization(areaConfig.getAppKey(), areaConfig.getMasterSecret());
                //NettyHttpClient nettyHttpClient = new NettyHttpClient(authCode,(HttpProxy) null,ClientConfig.getInstance());
                ApacheHttpClient apacheHttpClient = new ApacheHttpClient(authCode,(HttpProxy) null,ClientConfig.getInstance());
                jPushClientCustom.getPushClient().setHttpClient(apacheHttpClient);
                areaAppKeyPushClientMap.put(areaAppKey,Arrays.asList(jPushClientCustom));
            }
        }else{
            List<JPushClientCustom> jPushClients = areaAppKeyPushClientMap.get(areaAppKey);
            for(int i=0;i<jPushClients.size();i++){
                jPushClientCustom=jPushClients.get(i);
                if(jPushClientCustom.getCount()>=defaultJpushClientHandlerNum){
                    jPushClientCustom=null;
                }
            }
            if(jPushClientCustom==null){
                AreaConfig areaConfig = areaAppKeyConfigMap.get(areaAppKey);
                jPushClientCustom = new JPushClientCustom(areaConfig.getAppKey(), areaConfig.getMasterSecret());
                String authCode = ServiceHelper.getBasicAuthorization(areaConfig.getAppKey(), areaConfig.getMasterSecret());
                ApacheHttpClient apacheHttpClient = new ApacheHttpClient(authCode,(HttpProxy) null,ClientConfig.getInstance());
                jPushClientCustom.getPushClient().setHttpClient(apacheHttpClient);

                jPushClients.add(jPushClientCustom);
            }
        }
        jPushClientCustom.incr();
        System.out.println("count:"+jPushClientCustom.getCount());
        return jPushClientCustom;
    }

    public static boolean sendPlatformMessageWithAlias(String areaAppKey,String platformType,String msg,Map<String,String> parameter, String...alias){
       return sendPlatformMessageWithAlias(getJpushClient(areaAppKey),platformType,msg,parameter,alias);
    }

    public static boolean sendPlatformNotificationWithAlias(String areaAppKey,String platformType, String msg,Map<String,String> parameter, String...alias) {
        return sendPlatformNotificationWithAlias(getJpushClient(areaAppKey),platformType,msg,parameter,alias);
    }

    public static boolean broadcastPlatformMessageWithAlias(String areaAppKey,String platformType,String title, String msg){
        return broadcastPlatformMessageWithAlias(getJpushClient(areaAppKey),platformType,title,msg);
    }

    public static boolean broadcastPlatformNotificationWithAlias(String areaAppKey,String platformType,String msg) {
        return broadcastPlatformNotificationWithAlias(getJpushClient(areaAppKey),platformType,msg);
    }

    public static boolean sendPlatformMessageWithAlias(JPushClientCustom jPushClientCustom, String platformType, String msg,Map<String,String> parameter, String...alias){
        boolean flag = false;
        try {
            if(jPushClientCustom==null || platformType==null){
                log.error("jPushClientCustom==null || platformType==null");
                return flag;
            }
            PushResult result = null;
            Platform platform=getPlatform(platformType);

            if(platform!=null){
                Message.Builder builder = Message.newBuilder();
                if(parameter!=null && parameter.size()>0){
                    builder.addExtras(parameter);
                }
                PushPayload.Builder builder1 = PushPayload.newBuilder();
                boolean apnsProduction=true;
                if(Constants.jpushParameterConfig!=null){
                    apnsProduction=Constants.jpushParameterConfig.isApnsProduction();
                }
                if(apnsProduction){
                    builder1.setOptions(Options.newBuilder().setApnsProduction(true).build());
                }
                PushPayload pushPayload = builder1
                        .setPlatform(platform)
                        .setAudience(Audience.alias(alias))
                        .setMessage(builder.
                                setMsgContent(msg).
                                build())
                        .build();
                result=jPushClientCustom.sendPush(pushPayload);
            }
            log.info(platformType+"推送inner success end MessageWith Alias:"+Arrays.asList(alias));
            flag = true;
            if(log.isDebugEnabled())
                log.debug(String.format(platformType+" MessageWith result:%s",result!=null?JSONObject.toJSONString(result):"null"));
        } catch (APIConnectionException e) {
            log.error("Connection error. Should retry later. ");
        } catch (APIRequestException e) {
            log.error("Should review the error, and fix the request", e);
            log.error("HTTP Status: " + e.getStatus());
            log.error("Error Code: " + e.getErrorCode());
            log.error("Error Message: " + e.getErrorMessage());
        }finally{
            jPushClientCustom.decr();
            return flag;
        }
    }




    public static boolean broadcastPlatformMessageWithAlias(JPushClientCustom jPushClientCustom, String platformType, String title, String msg){
        boolean flag = false;
        try {
            if(jPushClientCustom==null || platformType==null){
                log.error("jPushClientCustom==null || platformType==null");
                return flag;
            }
            PushResult result = null;
            Platform platform=getPlatform(platformType);
            if(platform!=null){
                PushPayload build = PushPayload.newBuilder()
                        .setPlatform(platform)
                        .setAudience(Audience.all())
                        .setMessage(Message.newBuilder().setTitle(title).setMsgContent(msg).build())
                        .build();
                result=jPushClientCustom.sendPush(build);
            }
            log.info(platformType+"推送inner success end broad MessageWith ");
            flag = true;
            if(log.isDebugEnabled())
                log.debug(String.format(platformType+" broad MessageWith result:%s",result!=null?JSONObject.toJSONString(result):"null"));
        } catch (APIConnectionException e) {
            log.error("Connection error. Should retry later. ");
        } catch (APIRequestException e) {
            log.error("Should review the error, and fix the request", e);
            log.error("HTTP Status: " + e.getStatus());
            log.error("Error Code: " + e.getErrorCode());
            log.error("Error Message: " + e.getErrorMessage());
        }finally{
            jPushClientCustom.decr();
            return flag;
        }
    }


    public static boolean sendPlatformNotificationWithAlias(JPushClientCustom jPushClientCustom, String platformType,String msg, String...alias) {
        boolean flag = false;
        try {
            if (jPushClientCustom == null || platformType == null) {
                log.error("jPushClientCustom==null || platformType==null");
                return flag;
            }
            String format = String.format("{'msg':'%s','hashCode':%d}", msg, jPushClientCustom.hashCode());
            if(log.getLevel()==null){
                System.out.println(format);
            }
            PushResult result = null;
            Platform platform = getPlatform(platformType);
            boolean apnsProduction=true;
            if(Constants.jpushParameterConfig!=null){
                apnsProduction=Constants.jpushParameterConfig.isApnsProduction();
            }
            if (platform != null) {
                PushPayload.Builder builder = PushPayload.newBuilder();
                if(apnsProduction){
                    builder.setOptions(Options.newBuilder().setApnsProduction(true).build());
                }
                PushPayload build = builder
                        .setPlatform(platform)
                        .setAudience(Audience.alias(alias))
                        .setNotification(Notification.alert(msg))
                        .build();
                result = jPushClientCustom.sendPush(build);
            }
            log.info(platformType + "推送inner success end NotificationWith Alias:" + Arrays.asList(alias));
            flag = true;
            if (log.isDebugEnabled())
                log.debug(String.format(platformType + " NotificationWith result:%s", result != null ? JSONObject.toJSONString(result) : "null"));
        } catch (APIConnectionException e) {
            log.error("Connection error. Should retry later. ");
        } catch (APIRequestException e) {
            log.error("Should review the error, and fix the request", e);
            log.error("HTTP Status: " + e.getStatus());
            log.error("Error Code: " + e.getErrorCode());
            log.error("Error Message: " + e.getErrorMessage());
        } finally {
            jPushClientCustom.decr();
            return flag;
        }
    }

    public static boolean sendPlatformNotificationWithAlias(JPushClientCustom jPushClientCustom, String platformType,String msg, Map<String,String> parameter,String...alias) {
        if(parameter==null || parameter.size()<=0)
            return sendPlatformNotificationWithAlias(jPushClientCustom,platformType,msg,alias);
        boolean flag = false;
        try {
            if (jPushClientCustom == null || platformType == null) {
                log.error("jPushClientCustom==null || platformType==null");
                return flag;
            }
            String format = String.format("{'msg':'%s','hashCode':%d}", msg, jPushClientCustom.hashCode());
            if(log.getLevel()==null){
                System.out.println(format);
            }
            PushResult result = null;
            if (PLATFORM_ALL.equals(platformType)) {
                //发送给所有平台，带参数的需要挨个调用一遍
                log.info("PLATFORM_ALL sendPlatformNotificationWithAlias parameter");

                PushPayload build = getPushPayload(PLATFORM_IOS,msg,parameter,alias);
                result = jPushClientCustom.sendPush(build);
                if (log.isDebugEnabled())
                    log.debug(String.format(platformType + " NotificationWith result:%s", result != null ? JSONObject.toJSONString(result) : "null"));

                build = getPushPayload(PLATFORM_ANDROID,msg,parameter,alias);
                result = jPushClientCustom.sendPush(build);
                if (log.isDebugEnabled())
                    log.debug(String.format(platformType + " NotificationWith result:%s", result != null ? JSONObject.toJSONString(result) : "null"));

                build = getPushPayload(PLATFORM_WINPHONE,msg,parameter,alias);
                result = jPushClientCustom.sendPush(build);
                if (log.isDebugEnabled())
                    log.debug(String.format(platformType + " NotificationWith result:%s", result != null ? JSONObject.toJSONString(result) : "null"));
            } else{
                PushPayload build = getPushPayload(platformType,msg,parameter,alias);
                result = jPushClientCustom.sendPush(build);
                if (log.isDebugEnabled())
                    log.debug(String.format(platformType + " NotificationWith result:%s", result != null ? JSONObject.toJSONString(result) : "null"));
            }
            log.info(platformType + "推送inner success end NotificationWith Alias:" + Arrays.asList(alias));
            flag = true;
        } catch (APIConnectionException e) {
            log.error("Connection error. Should retry later. ");
        } catch (APIRequestException e) {
            log.error("Should review the error, and fix the request", e);
            log.error("HTTP Status: " + e.getStatus());
            log.error("Error Code: " + e.getErrorCode());
            log.error("Error Message: " + e.getErrorMessage());
        } finally {
            jPushClientCustom.decr();
            return flag;
        }
    }


    public static boolean broadcastPlatformNotificationWithAlias(JPushClientCustom jPushClientCustom, String platformType,String msg) {
        boolean flag = false;
        try {
            if (jPushClientCustom == null || platformType == null) {
                log.error("jPushClientCustom==null || platformType==null");
                return flag;
            }
            PushResult result = null;
            Platform platform = getPlatform(platformType);
            if (platform != null) {
                PushPayload build = PushPayload.newBuilder()
                        .setPlatform(platform)
                        .setAudience(Audience.all())
                        .setNotification(Notification.alert(msg))
                        .build();
                result = jPushClientCustom.sendPush(build);
            }
            log.info(platformType + "推送inner success end broad NotificationWith " );
            flag = true;
            if (log.isDebugEnabled())
                log.debug(String.format(platformType + " broad NotificationWith result:%s", result != null ? JSONObject.toJSONString(result) : "null"));
        } catch (APIConnectionException e) {
            log.error("Connection error. Should retry later. ");
        } catch (APIRequestException e) {
            log.error("Should review the error, and fix the request", e);
            log.error("HTTP Status: " + e.getStatus());
            log.error("Error Code: " + e.getErrorCode());
            log.error("Error Message: " + e.getErrorMessage());
        } finally {
            jPushClientCustom.decr();
            return flag;
        }
    }


    public static Platform getPlatform(String platformType){
        Platform platform = null;
        if (PLATFORM_ALL.equals(platformType)) {
            platform = Platform.all();
        } else if (PLATFORM_ANDROID.equals(platformType)) {
            platform = Platform.android();
        } else if (PLATFORM_IOS.equals(platformType)) {
            platform = Platform.ios();
        }else if (PLATFORM_WINPHONE.equals(platformType)) {
            platform = Platform.winphone();
        }
        return platform;
    }

    //这个是带参数的
    public static PushPayload getPushPayload(String platformType,String msg,Map<String,String> parameter,String...alias){
        PushPayload build=null;
        boolean apnsProduction=true;
        boolean content_available=false;
        String sound="default";
        if(Constants.jpushParameterConfig!=null){
            apnsProduction=Constants.jpushParameterConfig.isApnsProduction();
            content_available=Constants.jpushParameterConfig.isContent_available();
            sound = Constants.jpushParameterConfig.getSound();
        }
        if (PLATFORM_ANDROID.equals(platformType)) {
             build = PushPayload.newBuilder()
                    .setPlatform(Platform.android())
                    .setAudience(Audience.alias(alias))
                    .setNotification(Notification.newBuilder()
                            .addPlatformNotification(AndroidNotification.newBuilder()
                                    .setAlert(msg)
                                    .addExtras(parameter)
                                    .build())
                            .build())
                    .setOptions(Options.newBuilder()
                            .setApnsProduction(apnsProduction)
                            .build())
                    .build();
        } else if (PLATFORM_IOS.equals(platformType)) {
             build = PushPayload.newBuilder()
                    .setPlatform(Platform.ios())
                    .setAudience(Audience.alias(alias))
                    .setNotification(Notification.newBuilder()
                            .addPlatformNotification(IosNotification.newBuilder()
                                    .setAlert(msg)
                                    .setContentAvailable(content_available)
                                    .setSound(sound)
                                    .setBadge(1)
                                    .addExtras(parameter)
                                    .build())
                            .build())
                    .setOptions(Options.newBuilder()
                            .setApnsProduction(apnsProduction)
                            .build())
                    .build();
        }else if (PLATFORM_WINPHONE.equals(platformType)) {
             build = PushPayload.newBuilder()
                    .setPlatform(Platform.winphone())
                    .setAudience(Audience.alias(alias))
                    .setNotification(Notification.newBuilder()
                            .addPlatformNotification(WinphoneNotification.newBuilder()
                                    .setAlert(msg)
                                    .addExtras(parameter)
                                    .build())
                            .build())
                    .setOptions(Options.newBuilder()
                            .setApnsProduction(apnsProduction)
                            .build())
                    .build();
        }
        return build;
    }

}


